import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

/**
 * La classe Serveur fait parti du mini-projet "reservation".
 * Elle permet de gérer la persistance des données de deux manières différentes :
 * 	1) dans fichier de données contenant un ou des objet(s) java contenant les informations à stocker
 * 	2) fichier texte contenant des données textuelles dans un format de votre choix à définir
 * 
 * @author Maugard Matthieu & Paeta Suzy
 * @version V 1.0.0
 */

public class Serveur
{
	Serveur(){

	}

	//==========================================================================================================
	//================Méthodes communes ========================================================================
	//==========================================================================================================

	/**
	 * @param t
	 * @return
	 */
	public boolean restePlaces(Transport T, Trajet traj)
	{
		if(traj.getCapaciteCourante() == T.getCapaciteMax())
			return false;

		return true;
	}	
	//==========================================================================================================
	//================Méthodes pour la serialisation et deserialisation des objets==============================
	//==========================================================================================================

	public ArrayList<Transport> deserialiser(String filename) 
	{

		ObjectInputStream inputStream = null;
		ArrayList<Transport> trans = new ArrayList<Transport>();

		try 
		{

			//Construct the ObjectInputStream object
			inputStream = new ObjectInputStream(new FileInputStream(filename));

			Object obj = null;

			while ((obj = inputStream.readObject()) != null) 
			{

				if (obj instanceof Transport) 
				{
					trans.add((Transport)obj);
				}

			}


		} 
		catch (EOFException ex) 
		{ 
			//This exception will be caught when EOF is reached
		} 
		catch (ClassNotFoundException ex) 
		{
			ex.printStackTrace();
		} 
		catch (FileNotFoundException ex) 
		{
			ex.printStackTrace();
		} 
		catch (IOException ex) 
		{
			ex.printStackTrace();
		} 
		finally 
		{
			//Close the ObjectInputStream
			try 
			{
				if (inputStream != null) 
				{
					inputStream.close();
				}
			} 
			catch (IOException ex) 
			{
				ex.printStackTrace();
			}
		}
		return trans;
	}

	/**
	 * 
	 */
	public void serialiser(String filename, ArrayList<Transport> trans){

		ObjectOutputStream outputStream = null;

		try 
		{
			//Construct the LineNumberReader object
			outputStream = new ObjectOutputStream(new FileOutputStream(filename));

			for(int i=0 ; i < trans.size(); i++)
			{
				outputStream.writeObject(trans.get(i));
			}

		} 
		catch (FileNotFoundException ex) 
		{
			ex.printStackTrace();
		} 
		catch (IOException ex) 
		{
			ex.printStackTrace();
		} 
		finally 
		{
			//Close the ObjectOutputStream
			try 
			{
				if (outputStream != null) {
					outputStream.flush();
					outputStream.close();
				}
			} 
			catch (IOException ex) 
			{
				ex.printStackTrace();
			}
		}
	} 

	public void reservationObjet (Passager pass, Transport tr, Date dateDepart,String filename)
	{

		// verifier qu'il reste de la place
		ArrayList<Transport> trans = deserialiser(filename);

		for(int i = 0; i < trans.size(); i++)
		{
			if(trans.get(i).getNom().equals(tr.getNom()))
			{
				for(int j = 0; j < trans.get(i).getTrajets().size() ; j++)
				{
					if(trans.get(i).getTrajets().get(j).getDateDebut().equals(dateDepart))
					{
						if(restePlaces(trans.get(i),trans.get(i).getTrajets().get(j)))
						{
							trans.get(i).getTrajets().get(j).incrementeCapaciteCourante();
							trans.get(i).getTrajets().get(j).getPassagers().add(pass);
						}
						else
						{
							System.out.println("Transport complet !");
						}
					}
						
				}
			}
		}

		serialiser(filename,trans);

	}

	/**
	 * 
	 * @param filename
	 * @param type
	 * @return
	 */
	public ArrayList<Transport> getTransportsType(String filename, Types type){

		ArrayList<Transport> transports = deserialiser(filename);
		ArrayList <Transport> transportsType = new ArrayList<Transport>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			if(t.getType().equals(type)){
				transportsType.add(t);
			}
		}

		return transportsType;
	}



	/**
	 * 
	 * @param transports
	 * @param depart
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesDepart(String filename, Villes depart){

		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		ArrayList<Transport> transports = deserialiser(filename);
		Iterator it = transports.iterator();
		
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleDepart().equals(depart)){
					trajetVilles.add(trajet);
				}
			}
		}

		return trajetVilles;
	}

	/**
	 *
	 * @param transports
	 * @param arrivee
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesArrivee(String filename, Villes arrivee){

		ArrayList<Transport> transports = deserialiser(filename);
		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleArrivee().equals(arrivee)){
					trajetVilles.add(trajet);
				}	
			}
		}	
		return trajetVilles;
	}

	/**
	 * 
	 * @param transports
	 * @param depart
	 * @param arrivee
	 * @return tous les transports qui ont un trajet de depart à arrivee
	 */
	public ArrayList<Trajet> getTransportsVilles(String filename, Villes depart, Villes arrivee){
		ArrayList<Transport> transports = deserialiser(filename);
		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleDepart().equals(depart) && trajet.getVilleArrivee().equals(arrivee)){
					trajetVilles.add(trajet);
				}	
			}
		}	
		return trajetVilles;
	}

	/**
	 * 
	 * @param filename
	 * @param depart
	 * @param arrivee
	 * @param date
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesDateDepart(String filename, Villes depart, Villes arrivee, Date date){

		ArrayList<Transport> transports = deserialiser(filename);
		ArrayList<Trajet> trajetsVilles = new ArrayList<Trajet>();

		int annee = date.getYear();
		int mois = date.getMonth();
		int jour = date.getDate();
		int heure = date.getHours();
		int heurePlusDeux = heure+2; 
		int minutes = date.getMinutes();

		for(int i=0; i<transports.size(); i++){
			Transport trans = transports.get(i);
			ArrayList<Trajet> trajets = trans.getTrajets();

			for(int j=0; j<trajets.size(); j++){
				Trajet trajet = trajets.get(j);

				int anneeTrajet = date.getYear();
				int moisTrajet = date.getMonth();
				int jourTrajet = date.getDate();
				int heureTrajet = date.getHours();
				int minutesTrajet = date.getMinutes();

				if(trajet.getVilleDepart().equals(depart) && trajet.getVilleArrivee().equals(arrivee)){
					if((anneeTrajet == annee) && (moisTrajet == mois) && (jourTrajet == jour)){
						if((heureTrajet >= heure) && (heureTrajet <= heurePlusDeux)){
							trajetsVilles.add(trajet);
						}

					}
				}
			}
		}

		return trajetsVilles;
	}

	//==========================================================================================================
	//================Méthodes pour ecrire et lire dans un fichier =============================================
	//==========================================================================================================



	/**
	 * Ajouter une ligne dans le fichier
	 * @param texte à ecrire dans le fichier
	 * @param ecrireFin true si on veut écrire à la fin du fichier false pour écrire au début
	 */
	public void ecrire(String texte, boolean ecrireFin){

		try
		{
			//Instancier un FileWriter, en paramètre le nom du fichier et true signie
			//qu'on ajoute dans le fichier, on ecrase pas les données
			FileWriter fw = new FileWriter("donnees.txt", ecrireFin);

			//Instancier le BufferedWriter output auquel on donne comme
			//argument le FileWriter fw cree juste au dessus
			BufferedWriter output = new BufferedWriter(fw);

			//Ecrire dans le BufferedWriter le texte récupéré en paramètre
			output.write(texte);

			//Envoyer dans le fichier le BufferedWriter
			output.flush();

			output.close();
		}
		catch(IOException ioe){System.out.println("erreur : " + ioe );} 

		this.incrementeNbLignes();
	}

	/**
	 * Ajouter un Transport à la suite du fichier
	 * @param t le transport à ajouter
	 */
	public void ecrire(Transport t, boolean ecrireFin){

		ArrayList<Trajet> trajets= t.getTrajets();	

		for(int i=0; i<trajets.size(); i++){
			String resu = "";
			Trajet current = trajets.get(i);

			ArrayList<Passager> passagers = current.getPassagers();
			String passager = "";

			for(int j=0; j<passagers.size(); j++){
				passager += passagers.get(j).getName() + " " + passagers.get(j).getSurname() + "-";

			}

			String dateDebut = current.getDateDebut().getDate() + ":" + current.getDateDebut().getMonth() + ":" + (current.getDateDebut().getYear()+1900);
			dateDebut += ":" + current.getDateDebut().getHours() + ":" + current.getDateDebut().getMinutes();

			String dateFin = current.getDateFin().getDate() + ":" + current.getDateFin().getMonth() + ":" + (current.getDateFin().getYear()+1900);
			dateFin += ":" + current.getDateFin().getHours() + ":" + current.getDateFin().getMinutes();

			resu += t.getType().toString();
			resu += ";";
			resu += t.getNom();
			resu += ";";
			resu += t.getCapaciteMax();
			resu += ";";
			resu += current.getCapaciteCourante();
			resu += ";";
			resu += current.getVilleDepart().toString();
			resu += ";";
			resu += current.getVilleArrivee().toString();
			resu += ";";
			resu += dateDebut;
			resu += ";";
			resu += dateFin;
			resu += ";";
			resu += passager;

			resu += "\n";
			ecrire(resu, ecrireFin);
		}


	}



	/**
	 * Mettre dans un tableau de String toutes les lignes du fichiers
	 * @return
	 */
	public String[] lire(){

		String[] texte = null;
		try{
			String nomFichier = "donnees.txt";

			//Instancier un FileReader pour lire le fichier souhaiter
			FileReader fr = new FileReader(nomFichier);

			//Instancier un BufferReader
			//
			BufferedReader br = new BufferedReader(fr);

			//Ranger les lignes du fichier dans le tableau
			texte = new String[this.getNbLignesFichier()];
			int i = 0;

			while(i<this.getNbLignesFichier()){
				texte[i] = br.readLine(); 
				i++;
			}

			br.close();
		}
		catch(IOException ioe){System.out.println("erreur : " + ioe);} 

		return texte;
	}


	private void incrementeNbLignes() {

		int i = getNbLignesFichier() +1;
		setNbLignes(i);

	}

	private void decrementeNbLignes() {
		int i = getNbLignesFichier() +1;
		setNbLignes(i);

	}

	private int getNbLignesFichier() {
		int i=0;
		ObjectInputStream inputStream = null;
		ArrayList<Transport> trans = new ArrayList<Transport>();

		try 
		{

			//Construct the ObjectInputStream object
			inputStream = new ObjectInputStream(new FileInputStream("lignes.ser"));

			Object obj = null;

			while ((obj = inputStream.readObject()) != null) 
			{

				if (obj instanceof Integer) 
				{

					i = (Integer)obj;
				}

			}


		} 
		catch (EOFException ex) 
		{ 
			//This exception will be caught when EOF is reached
		} 
		catch (ClassNotFoundException ex) 
		{
			ex.printStackTrace();
		} 
		catch (FileNotFoundException ex) 
		{
			//ex.printStackTrace();
		} 
		catch (IOException ex) 
		{
			ex.printStackTrace();
		} 
		finally 
		{
			//Close the ObjectInputStream
			try 
			{
				if (inputStream != null) 
				{
					inputStream.close();
				}
			} 
			catch (IOException ex) 
			{
				ex.printStackTrace();
			}
		}
		return i;
	}

	private void setNbLignes(int i) {
		ObjectOutputStream outputStream = null;

		try 
		{
			//Construct the LineNumberReader object
			outputStream = new ObjectOutputStream(new FileOutputStream("lignes.ser"));



			outputStream.writeObject(i);


		} 
		catch (FileNotFoundException ex) 
		{
			ex.printStackTrace();
		} 
		catch (IOException ex) 
		{
			ex.printStackTrace();
		} 
		finally 
		{
			//Close the ObjectOutputStream
			try 
			{
				if (outputStream != null) {
					outputStream.flush();
					outputStream.close();
				}
			} 
			catch (IOException ex) 
			{
				ex.printStackTrace();
			}
		}

	}

	/**
	 * Supprimer une ligne dans le fichier
	 * @param numLigne la ligne à supprimer
	 */
	public void supprimerLigne(int numLigne){
		String[] lecture = lire();
		String aEcrire = "";

		for(int i = 0; i<lecture.length; i++){
			if (i!=numLigne-1){

				ecrire(lecture[i], false);

			}
		}		

		decrementeNbLignes();		

	}


	/**
	 * 
	 */
	public void supprimerTout(){
		setNbLignes(0);
		ecrire("", false);
	}



	/**
	 * Recréer tous les transports du fichier
	 * @return une ArrayList<Transport> contenant tous les transports du fichier
	 */
	public ArrayList<Transport> reconstruireTransports(){

		ArrayList<Transport> transports = new ArrayList<Transport>();


		//Mettre chaque ligne du fichier dans une case du tableau
		String[] lecture = lire();

		//Délimiteurs de chaines
		String delimiteur = ";";
		String delimiteur2 = ":";
		String delimiteur3 = "-";
		String delimiteur4 = " ";

		//Parcourir toutes les lignes du fichier à l'aide du tableau
		for(int i=0; i<lecture.length; i++){
			ArrayList<Passager> passagersList = new ArrayList<Passager>();
			//Mettre dans un tableau les différents champs de la ligne (séparés par un ;)
			if(lecture[i] != null){
				String[] ligneCourante = lecture[i].split(delimiteur);

				//Recréer les objets correspondants
				Types type = (Types)Enum.valueOf(Types.class, ligneCourante[0]);
				String nom = ligneCourante[1];
				int capaciteMax = Integer.valueOf(ligneCourante[2]).intValue();			
				int capaciteCourante = Integer.valueOf(ligneCourante[3]).intValue();
				Villes villeDepart = (Villes)Enum.valueOf(Villes.class, ligneCourante[4]);	
				Villes villeArrivee = (Villes)Enum.valueOf(Villes.class, ligneCourante[5]);

				//Recréer les dates
				String dateD[] = ligneCourante[6].split(delimiteur2); 
				int jourD = Integer.valueOf(dateD[0]).intValue();
				int moisD = Integer.valueOf(dateD[1]).intValue();
				int anneeD = Integer.valueOf(dateD[2]).intValue() - 1900;
				int heureD = Integer.valueOf(dateD[3]).intValue();
				int minutesD = Integer.valueOf(dateD[4]).intValue();
				Date dateDebut = new Date(anneeD, moisD, jourD, heureD, minutesD);

				String dateF[] = ligneCourante[7].split(delimiteur2); 
				int jourF = Integer.valueOf(dateF[0]).intValue();
				int moisF = Integer.valueOf(dateF[1]).intValue();
				int anneeF = Integer.valueOf(dateF[2]).intValue() - 1900;
				int heureF = Integer.valueOf(dateF[3]).intValue();
				int minutesF = Integer.valueOf(dateF[4]).intValue();
				Date dateFin = new Date(anneeF, moisF, jourF, heureF, minutesF);


				if(ligneCourante.length>8){

					String[] passagers = ligneCourante[8].split(delimiteur3);
					for(int l=0; l<passagers.length; l++){

						String[] nomPrenom = passagers[l].split(delimiteur4);
						if(passagers.length == 2){
							String name = nomPrenom[0];
							String surname = nomPrenom[1];

							passagersList.add(new Passager(name, surname));
						}
					}
				}

				//Récréer le trajet puis le transport et l'ajouter à l'ArrayList
				Trajet trajet = new Trajet(villeDepart, villeArrivee, dateDebut, dateFin);
				trajet.setPassagers(passagersList);
				trajet.setCapaciteCourante(capaciteCourante);

				ArrayList<Trajet> trajets = new ArrayList<Trajet>();
				trajets.add(trajet);
				Transport transport = new Transport(nom, capaciteMax, trajets, type);
				transports.add(transport);
			}
		}

		for(int j=0; j<transports.size(); j++){
			for(int k=0; k<transports.size(); k++){
				if(j!=k){
					if(transports.get(j).getNom().equals(transports.get(k).getNom())){
						ArrayList<Trajet> trajets = transports.get(k).getTrajets();
						if(trajets.size()==1){
							transports.get(j).ajouterTrajet(trajets.get(0));
							transports.remove(k);
						}
					}
				}
			}
		}
		return transports;
	}

	public void reservationFichier(Passager pass, Transport tr, Date dateDepart)
	{
		ArrayList<Transport> trans = reconstruireTransports();

		for(int i = 0; i < trans.size(); i++)
		{
			if(trans.get(i).getNom().equals(tr.getNom()))
			{
				

					for(int j = 0; j < trans.get(i).getTrajets().size() ; j++)
					{
						if(trans.get(i).getTrajets().get(j).getDateDebut().equals(dateDepart))
						{
							if(restePlaces(trans.get(i),trans.get(i).getTrajets().get(j)))
							{
								trans.get(i).getTrajets().get(j).incrementeCapaciteCourante();
								trans.get(i).getTrajets().get(j).getPassagers().add(pass);
							}
							else
							{
								System.out.println("Reservation impossible, places insuffisantes");
							}
						}
					}
				
			}
		}

		supprimerTout();
		for(int j=0; j<trans.size(); j++){
			ecrire(trans.get(j), true);
		}

	}

	public ArrayList<Transport> getTransportsTypeFichier(Types type){

		ArrayList<Transport> transports = reconstruireTransports();
		ArrayList <Transport> transportsType = new ArrayList<Transport>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			if(t.getType().equals(type)){

				transportsType.add(t);
			}
		}

		return transportsType;
	}
	
	
	/**
	 * 
	 * @param transports
	 * @param depart
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesDepartFichier(Villes depart){

		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		ArrayList<Transport> transports = reconstruireTransports();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleDepart().equals(depart)){
					trajetVilles.add(trajet);
				}
			}
		}

		return trajetVilles;
	}

	/**
	 *
	 * @param transports
	 * @param arrivee
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesArriveeFichier(Villes arrivee){

		ArrayList<Transport> transports = reconstruireTransports();
		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleArrivee().equals(arrivee)){

					trajetVilles.add(trajet);
				}	
			}
		}	
		return trajetVilles;
	}

	/**
	 * 
	 * @param transports
	 * @param depart
	 * @param arrivee
	 * @return tous les transports qui ont un trajet de depart à arrivee
	 */
	public ArrayList<Trajet> getTransportsVillesFichier(Villes depart, Villes arrivee){
		ArrayList<Transport> transports = reconstruireTransports();
		ArrayList <Trajet> trajetVilles = new ArrayList<Trajet>();
		Iterator it = transports.iterator();
		while(it.hasNext()){
			Transport t = (Transport)it.next();
			ArrayList<Trajet> trajets = t.getTrajets();

			for(int i=0; i<trajets.size(); i++){
				Trajet trajet = trajets.get(i);
				if(trajet.getVilleDepart().equals(depart) && trajet.getVilleArrivee().equals(arrivee)){

					trajetVilles.add(trajet);
				}	
			}
		}	
		return trajetVilles;
	}

	/**
	 * 
	 * @param filename
	 * @param depart
	 * @param arrivee
	 * @param date
	 * @return
	 */
	public ArrayList<Trajet> getTransportsVillesDateDepartFichier(Villes depart, Villes arrivee, Date date){

		ArrayList<Transport> transports = reconstruireTransports();
		ArrayList <Trajet> trajetsVilles = new ArrayList<Trajet>();

		int annee = date.getYear();
		int mois = date.getMonth();
		int jour = date.getDate();
		int heure = date.getHours();
		int heurePlusDeux = heure+2; 
		int minutes = date.getMinutes();

		for(int i=0; i<transports.size(); i++){
			Transport trans = transports.get(i);
			ArrayList<Trajet> trajets = trans.getTrajets();

			for(int j=0; j<trajets.size(); j++){
				Trajet trajet = trajets.get(j);

				int anneeTrajet = date.getYear();
				int moisTrajet = date.getMonth();
				int jourTrajet = date.getDate();
				int heureTrajet = date.getHours();
				int minutesTrajet = date.getMinutes();

				if(trajet.getVilleDepart().equals(depart) && trajet.getVilleArrivee().equals(arrivee)){
					if((anneeTrajet == annee) && (moisTrajet == mois) && (jourTrajet == jour)){
						if((heureTrajet > heure) && (heureTrajet < heurePlusDeux)){
							trajetsVilles.add(trajet);
						}

					}
				}
			}
		}

		return trajetsVilles;
	}

}
